package com.ilikesoup.instant;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ilikesoup.instant.empty.EmptyUtils;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.lang.NonNull;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 封装常用的 Stream 操作，简化代码
 */
public class CollectionUtils {

    private CollectionUtils() {
        throw new RuntimeException("Must not be instantiated!");
    }

    public static <T, R> List<R> mapping(Collection<T> source, Function<T, R> mapper) {
        if(EmptyUtils.isEmpty(source)) {
            return new ArrayList<>(0);
        }
        return source.stream().map(mapper).collect(Collectors.toList());
    }

    public static <T, R> List<R> mappingThenDistinct(Collection<T> source, Function<T, R> mapper) {
        if(EmptyUtils.isEmpty(source)) {
            return new ArrayList<>(0);
        }
        return source.stream().map(mapper).distinct().collect(Collectors.toList());
    }

    public static <T, K> Map<K, List<T>> grouping(Collection<T> source, Function<T, K> groupingKey) {
        if(EmptyUtils.isEmpty(source)) {
            return new HashMap<>(0);
        }
        return source.stream().collect(Collectors.groupingBy(groupingKey));
    }

    public static <T, K, V> Map<K, V> toMap(Collection<T> source, Function<T, K> keyGetter, Function<T, V> valueGetter) {
        if(EmptyUtils.isEmpty(source)) {
            return new HashMap<>(0);
        }
        return source.stream().collect(Collectors.toMap(keyGetter, valueGetter, (a, b) -> b));
    }

    public static <T, K> Map<K, T> toMap(Collection<T> source, Function<T, K> keyGetter) {
        if(EmptyUtils.isEmpty(source)) {
            return new HashMap<>(0);
        }
        return source.stream().collect(Collectors.toMap(keyGetter, Function.identity(), (a, b) -> b));
    }

    public static <T> List<T> splitAndCast(String content, Function<String, T> castFunc) {
        return Stream.of(content.split(",")).map(castFunc).collect(Collectors.toList());
    }

    /**
     * 让第一个集合“吃掉”第二个集合的数据。
     * 一人吃掉一个，必须提供充足的食物。第二个集合的数据长度要大于第一个集合的长度。
     */
    public static <T, E> void eat(List<T> men, List<E> foods, BiConsumer<T, E> eating) {
        if(men.size() < foods.size()) {
            return;
        }
        for (int i = 0; i < men.size(); i++) {
            eating.accept(men.get(i), foods.get(i));
        }
    }

    public static <T> String join(Collection<T> list) {
        return list.stream().map(Objects::toString).collect(Collectors.joining(","));
    }

    public static <T> String join(Collection<T> list, Function<T, String> toStringFunc, String delimiter) {
        if(EmptyUtils.isEmpty(list)) {
            return "";
        }
        return list.stream().map(toStringFunc).collect(Collectors.joining(delimiter));
    }

    public static List<String> splitAndTrim(String str) {
        return splitAndTrim(str, ArrayList::new);
    }

    public static <T extends Collection<String>> T splitAndTrim(String str, Supplier<T> collectionSupplier) {
        T collection = collectionSupplier.get();
        if(StringUtils.isBlank(str)) {
            return collection;
        }
        String[] parts = str.trim().split(",");
        for (String part : parts) {
            if(!StringUtils.isBlank(part)) {
                collection.add(part.trim());
            }
        }
        return collection;
    }

    /**
     * list去重，取决于 T 的 equals
     *
     * @see Object#equals
     * @param origin
     * @param <T>
     * @return
     */
    public static <T> List<T> distinct(List<T> origin) {
        if(EmptyUtils.isEmpty(origin)) {
            return origin;
        }
        return origin.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 通过 key 去重，取决于 K 的 equals
     *
     * @see Object#equals
     * @param origin
     * @param keyGetter
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T, K> List<T> distinctByKey(List<T> origin, Function<T, K> keyGetter) {
        final Map<K, Boolean> map = new ConcurrentHashMap<>();
        return origin.stream().filter(o -> map.put(keyGetter.apply(o), Boolean.TRUE) == null).collect(Collectors.toList());
    }


}
